#include "CompresorGamma.h"
#include "CompresorUnario.h"
#include <math.h>
#include <iostream>
#include "string.h"

CompresorGamma::CompresorGamma() {}

// Compresor de números decimales a códigos gamma devuelve la long en bits de código
// Dentro del mismo se alloca memoria a gamma
int CompresorGamma::comprimir(unsigned int x, unsigned char** gamma) {
	if(x == 0)
		return -1;
	unsigned int long_gamma = 0;
	unsigned int num_unario = (log10(x) / log10(2)) + 1;
	//std::cout << "Numero a unario: " << num_unario << std::endl;
	unsigned int long_cod_binario = num_unario - 1;
	unsigned int cant_bytes_binario = 0; // cantidad de bytes usados para el num binario
	unsigned int cant_bytes_unario = 0; // cantidad de bytes usados para el num unario
	unsigned int cant_bytes_gamma = 0; // cantidad de bytes usados para el num gamma
	unsigned int aux = pow(2, long_cod_binario);
	unsigned int num_binario = x - aux; // unsigned short puedo representar hasta 65535
	//std::cout << "Numero a binario: " << num_binario << std::endl;
	//std::cout << "longitud binario: " << long_cod_binario << std::endl;
	unsigned char *unario = NULL;
	long_gamma = num_unario + long_cod_binario;
	//std::cout << "longitud gamma: " << long_gamma << std::endl;
	if(long_cod_binario % 8 == 0)
		cant_bytes_binario = long_cod_binario / 8;
	else
		cant_bytes_binario = (long_cod_binario / 8) + 1;

	if(num_unario % 8 == 0)
		cant_bytes_unario = num_unario / 8;
	else
		cant_bytes_unario = (num_unario / 8) + 1;

	if(long_gamma % 8 == 0) {
		cant_bytes_gamma = long_gamma / 8;

	}
	else {
		cant_bytes_gamma = long_gamma / 8 + 1;
	}

	(*gamma) = new unsigned char[cant_bytes_gamma];
	//(*gamma) = NULL;

	//for(unsigned int i = 0; i < cant_bytes_gamma; i++)
		//(*gamma)[i] = 0;

	CompresorUnario c_u;
	c_u.comprimir(num_unario, &unario);
	// Almaceno el código unario en el vector q representa el código gamma
	for(unsigned int i = 1; i <= cant_bytes_unario; i++) {
		(*gamma)[i-1] = unario[i-1];
	//	std::cout << "Byte " << i-1 << ": " << c_u->contador_de_1((*gamma)[i - 1]) << std::endl;
	}

	//std::cout  << "Código unario: " << *((unsigned int*)(*gamma)) << std::endl;

	// Desplazo el código unario a la izquierda
	*((unsigned int*)(*gamma)) = (*(unsigned int*)(*gamma)) << long_cod_binario;
	//*((unsigned int*)(*gamma)) <<= long_cod_binario; // x <<= a igual a x = x << a

	// std::cout  << "Numero binario: " << *((unsigned int*)(*gamma)) << std::endl;

	for(unsigned int i = 1; i <= cant_bytes_binario; i++) {
		(*gamma)[i-1] |= (num_binario >> (8 * (i-1))); // Desplazamiento de 8 bits por byte
		//std::cout << "Byte " << i-1 << ": " << c_u.contador_de_1((*gamma)[i - 1]) << std::endl;
	}

	//std::cout << "Byte " << 1 << ": " << c_u.contador_de_1((*gamma)[1]) << std::endl;

	//std::cout  << "Numero en codgamma: " << *((unsigned int*)(*gamma)) << std::endl;
	delete[] unario; // Dentro de comprimir se pide memoria para unario
	return long_gamma;
}

int CompresorGamma::descompresor(unsigned char *gamma) {
	unsigned int cant_bytes = strlen((char *)gamma);
	unsigned int mascara = 0;
	unsigned int num_unario = 1;
	unsigned int num_bin = 0;
	unsigned int num_gamma = 0;
	unsigned int aux = *(unsigned int *)(gamma);
	bool dio_1 = false;
	bool fin_unario = false;
	//std::cout  << "Numero binario: " << aux << std::endl;
	for(int i = ((cant_bytes * 8) - 1); i >= 0; i--) {
		mascara = pow(2, i);
		if((mascara & aux) == 0) {
			if(dio_1) {
				fin_unario = true;
			}
		}
		if((mascara & aux) == mascara) {
			if(!fin_unario) {
				dio_1 = true;
				num_unario++;
			}
			else {
				num_bin += mascara;
			}
		}
	}

	//std::cout  << "Numero unario: " << num_unario << std::endl;
	//std::cout  << "Numero binario: " << num_bin << std::endl;
	num_gamma = pow(2, num_unario - 1) + num_bin;
	return num_gamma;
}


CompresorGamma::~CompresorGamma() {}
